// TransparentWindow.m
// By Jayson Adams, NeXT Developer Support Team
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.

// modified by Miyai, ISR	1992.7.28

#import <math.h>
#import <string.h>
#import <dpsclient/wraps.h>
#import <appkit/publicWraps.h>
#import <appkit/NXImage.h>
#import <appkit/Window.h>
#import <appkit/Application.h>

#import "pswraps.h"
#import "AcceptWindow.h"
#import "Animator.h"

#import "TransparentWindow.h"
#import "errdebug.h"

#define NOWINDOW 0


@implementation TransparentWindow


- initForImage:anImage at:(NXPoint *)windowOrigin forView:anObject
{
    NXRect	windowFrame;
    NXPoint	origin = {0.0, 0.0};
    
  /*
   * create an offscreen window with the image (for use in our pswrap routines)
   */
    [anImage getSize:&windowSize];
    windowFrame.origin.x = windowFrame.origin.y = 0.0;
    windowFrame.size = windowSize;
    image = [[Window alloc] initContent:&windowFrame
			    style:NX_PLAINSTYLE
			    backing:NX_RETAINED
			    buttonMask:0
			    defer:NO];

  /* copy the image to our new window */
    [[image contentView] lockFocus];
    [anImage composite:NX_COPY toPoint:&origin];
    [[image contentView] unlockFocus];
    
  /* save the object that created us */
    source = anObject;
    
//    iIconImage = [anObject image];
    iIconImage = anImage;
    if([anObject respondsTo:@selector(title)]) {
        strncpy (iTitle, [anObject title], sizeof(iTitle)-1);
    }
  /*
   * create an animator to move our transparent window back to its origin if
   * the user lets go of the window over something that won't accept it;  we'll
   * create it now so there's no delay when we really need it
   */
    animator = [[Animator alloc] initChronon:(1.0 / 40.0)
				 adaptation:0.0
				 target:self
				 action:@selector(animateBack:)
				 autoStart:NO
				 eventMask:0];

    iDefaultAccepted = NO;

    return self;
}

- free
{
    [image free];
    [animator free];
    
    return [super free];
}

- imageWindow
{
    return image;
}

- image
{
    return iIconImage;
}
   
- source
{
    return source;
}
   
- (const char *)title
{
    return (const char *)iTitle;
}

- dragFromMouseDown:(NXPoint *)startingPoint mouseOffset:(NXPoint *)offset
{
    int		    mouseUp = 0, windowUnderImage, windowUnderMouseChanged = 0,
		    previousWindowUnderMouseChanged = 0;
    unsigned int    windowNum;
//    BOOL	    accepted = NO;
    BOOL	    accepted;
    float	    deltaX, deltaY, distance;
    
    // set default for accepted
    // iDefaultAccepted : NO: for IFolderTransparentWindow
    //			  YES: for TransparentWindow
    
    accepted = iDefaultAccepted;
    
  /* disable the wait cursor for our dragging loop */
    PSsetwaitcursorenabled(NO);

  /* get the image's gstate for our dragWindow pswrap */
    imageGstate = [image gState];
    
    currentPoint = stoppingPoint = *startingPoint;
    
  /* set everything up for the drag loop */
    initDrag(currentPoint.x, currentPoint.y, offset->x, offset->y,
    	     windowSize.width, windowSize.height, &iWindow, &bgWindow,
	     &niWindow, &igstate, &bggstate, &nigstate, &gWindow, &ggstate);

    while (!mouseUp) {
      /* call the pswrap that moves the transparent window */
	dragWindow(iWindow, niWindow, gWindow, windowUnderMouseChanged,
      		   previousWindowUnderMouseChanged, igstate, bggstate,
		   nigstate, imageGstate, ggstate, currentPoint.x,
		   currentPoint.y, offset->x, offset->y, windowSize.width,
		   windowSize.height, &mouseUp, &windowUnderImage,
		   &(currentPoint.x), &(currentPoint.y));
	
	[self checkForAcceptWindow:windowUnderImage
	      atPoint:&currentPoint
      		     :&windowUnderMouseChanged
		     :&previousWindowUnderMouseChanged];
    }

  /*
   * flush window's because checkForAcceptWindow might've caused windows to
   * change and the user may have let go of the mouse, making mouseUp true and
   * preventing dragWindow from getting called (which does the flushWindows
   * normally)
   */
    if (windowUnderMouseChanged) {
	NXConvertGlobalToWinNum(windowUnderMouseChanged, &windowNum);
	[[NXApp findWindow:windowNum] flushWindow];
    }

    DBG(1,fprintf(stderr,"     previousWindowUnderMouse = %d\n",
					(int)previousWindowUnderMouse));
  /*
   * if there was a window under the mouse point, flush it for the reasons
   * above, plus tell it the user dropped the image on it
   */
    if (previousWindowUnderMouse) {
	if (previousWindowUnderMouseChanged) {
	    [previousWindowUnderMouse flushWindow];
	}
//	accepted = [previousWindowUnderMouse windowDropped:&currentPoint
//					     fromSource:source];
	accepted = [previousWindowUnderMouse windowDropped:&currentPoint
					     fromSource:self];
    }
    
  /* turn the wait cursor back on */
    PSsetwaitcursorenabled(YES);
    
  /*
   * if the user didn't drop the window over an accepting window, or the
   * window didn't accept it, animate the transparent window back to its origin
   */
    if (!accepted) {
	deltaX = currentPoint.x - startingPoint->x;
	deltaY = currentPoint.y - startingPoint->y;
	if (deltaX && deltaY) {
	    distance = sqrt(deltaX  * deltaX + deltaY * deltaY);
	    frames = ceil(40.0 * distance / 5000.0);
	    if (frames == 1) {
	      /* make an exception for windows close to their origin */
		frames = ceil(40.0 * distance / 1400.0);
	    }
	    increment.x = deltaX / frames;
	    increment.y = deltaY / frames;
	
	  /* we need to remember the mouse offset */
	    mouseOffset = *offset;
	    [animator startEntry];
	    
	    [self preambleForAccepted:accepted];	//accepted = NO;
	    
	    return self;
	}
    }
    
  /* destroy the windows and gstates used to drag the TransparentWindow */
    cleanup(iWindow, bgWindow, niWindow, gWindow, igstate, bggstate, nigstate,
    	    ggstate);
    
    return [self free];
}

- animateBack:sender
{
  /* compute the transparent window's new location */
    currentPoint.x -= increment.x;
    currentPoint.y -= increment.y;
    
  /* see if new location is close enough to the final position */
    if ((increment.x > 0 && (currentPoint.x < stoppingPoint.x + 3.0)) ||
      (increment.x < 0 && (currentPoint.x > stoppingPoint.x - 3.0))) {
	currentPoint.x = stoppingPoint.x;
    }
    if ((increment.y > 0 && (currentPoint.y < stoppingPoint.y + 3.0)) ||
      (increment.y < 0 && (currentPoint.y > stoppingPoint.y + 3.0))) {
	currentPoint.y = stoppingPoint.y;
    }

  /* move it there */
    miniDragWindow(iWindow, niWindow, gWindow, igstate, bggstate, nigstate,
    		   imageGstate, ggstate, currentPoint.x, currentPoint.y,
		   mouseOffset.x, mouseOffset.y, windowSize.width,
		   windowSize.height);
    
  /* count down the number of frames */
    if (--frames) {
	return self;
    }
    
  /* all done */
    [animator stopEntry];
    cleanup(iWindow, bgWindow, niWindow, gWindow, igstate, bggstate, nigstate,
    	    ggstate);
    
    return [self free];
}

- checkForAcceptWindow:(int)windowNumUnderMouse atPoint:(NXPoint *)point
  :(int *)windowUnderMouseChanged :(int *)previousWindowUnderMouseChanged
{
    unsigned   int	windowNum;
    id			windowUnderMouse = NOWINDOW;
    
    *windowUnderMouseChanged = 0;
    *previousWindowUnderMouseChanged = 0;
      
  /* find the window object under the mouse point */
    if (windowNumUnderMouse) {
	NXConvertGlobalToWinNum(windowNumUnderMouse, &windowNum);
	windowUnderMouse = [NXApp findWindow:windowNum];
	if (![windowUnderMouse
			   respondsTo:@selector(windowEntered:fromSource:)]) {
	    windowUnderMouse = NOWINDOW;
	    windowNumUnderMouse = 0;
	}
    }
    
  /*
   * tell the window previously under the mouse (if different) that the image
   * exited
   */
    if (windowUnderMouse != previousWindowUnderMouse &&
	previousWindowUnderMouse != NOWINDOW) {
//	if ([previousWindowUnderMouse windowExited:source]) {
	if ([previousWindowUnderMouse windowExited:self]) {
	    *previousWindowUnderMouseChanged = previousWindowNumUnderMouse;
	}
    }
    
  /* tell the window under the mouse (if it exists) that the image entered */
    if (windowUnderMouse != NOWINDOW) {
//	if ([windowUnderMouse windowEntered:point fromSource:source]) {
	if ([windowUnderMouse windowEntered:point fromSource:self]) {
	    *windowUnderMouseChanged = windowNumUnderMouse;
	}
    }
    
    previousWindowUnderMouse = windowUnderMouse;
    previousWindowNumUnderMouse = windowNumUnderMouse;
    
    return self;
}

- (BOOL)defaultAccepted
{
    return iDefaultAccepted;
}

- preambleForAccepted:(BOOL)flag
{
    // should be overriden by its subclass
    return self;
}


@end
